﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.IO;
using System.IO.Ports;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
using System.Collections.Concurrent;

using UniLua;

namespace SerialHelper
{
    public partial class frmMain : Form
    {
        private SerialPort serial = new SerialPort();
        private System.Timers.Timer time_timer = new System.Timers.Timer();
        private System.Timers.Timer loop_timer = new System.Timers.Timer();
        private System.Timers.Timer recv_timer = new System.Timers.Timer();

        private int in_loop_timer = 0;
        private int in_recv_timer = 0;

        private int tn_show_time = 0;
        private long count_send = 0, count_recv = 0;
        private string recv_file_path, send_file_path;

        private bool _recv_file = false;

        private bool _file_should_stop = false;
        private System.Threading.Thread file_thread = null;

        private bool _lua_should_stop = false;
        private System.Threading.Thread lua_thread = null;

        private bool _group_should_stop = false;
        private System.Threading.Thread group_thread = null;

        private AppConfig config = new AppConfig();

        private ILuaState lua;

        private ConcurrentQueue<byte> read_buffer = new ConcurrentQueue<byte>();
        private ConcurrentQueue<byte[]> serial_recv_buffer = new ConcurrentQueue<byte[]>();

        ToolTip tooltip_code = new ToolTip();
        Encoding encoder = Encoding.Default;

        #region 检测USB串口的热拔插
        // usb消息定义
        public const int WM_DEVICE_CHANGE = 0x219;
        public const int DBT_DEVICEARRIVAL = 0x8000;
        public const int DBT_DEVICE_REMOVE_COMPLETE = 0x8004;
        public const UInt32 DBT_DEVTYP_PORT = 0x00000003;
        [StructLayout(LayoutKind.Sequential)]
        struct DEV_BROADCAST_HDR
        {
            public UInt32 dbch_size;
            public UInt32 dbch_devicetype;
            public UInt32 dbch_reserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        protected struct DEV_BROADCAST_PORT_Fixed
        {
            public uint dbcp_size;
            public uint dbcp_devicetype;
            public uint dbcp_reserved;
            // Variable?length field dbcp_name is declared here in the C header file.
        }

        /// <summary>
        /// 检测USB串口的拔插
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            DEV_BROADCAST_HDR dbhdr;
            if (m.Msg == WM_DEVICE_CHANGE)        // 捕获USB设备的拔出消息WM_DEVICECHANGE
            {
                switch (m.WParam.ToInt32())
                {
                case DBT_DEVICE_REMOVE_COMPLETE:    // USB拔出  
                    dbhdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
                    if (dbhdr.dbch_devicetype == DBT_DEVTYP_PORT)
                    {
                        string portName = Marshal.PtrToStringUni((IntPtr)(m.LParam.ToInt32() + Marshal.SizeOf(typeof(DEV_BROADCAST_PORT_Fixed))));
                        Console.WriteLine("Port '" + portName + "' removed.");

                        if (portName.CompareTo(serial.PortName) == 0)
                        {
                            serial_port_close();
                        }
                    }
                    break;
                case DBT_DEVICEARRIVAL:             // USB插入获取对应串口名称
                    dbhdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
                    if (dbhdr.dbch_devicetype == DBT_DEVTYP_PORT)
                    {
                        string portName = Marshal.PtrToStringUni((IntPtr)(m.LParam.ToInt32() + Marshal.SizeOf(typeof(DEV_BROADCAST_PORT_Fixed))));
                        Console.WriteLine("Port '" + portName + "' arrived.");
                    }
                    break;
                }
            }
            base.WndProc(ref m);
        }
        #endregion

        public frmMain()
        {
            InitializeComponent();

            DoubleBuffer.SetDoubleBuffer(list_group_list, true);

            //////////////////////////////////////////////////////////////////////////////

            string[] checks = { "XOR异或校验", "SUM8累加校验", "ModBusLRC校验", "CRC16低字节在前", "CRC16高字节在前", "SUM16低字节在前", "SUM16高字节在前", "ModBusCRC低字节在前", "ModBusCRC高字节在前" };
            cbo_pack_check.Items.AddRange(checks);
            cbo_pack_check.Text = "XOR异或校验";

            string[] port_names = SerialPort.GetPortNames();
            Array.Sort(port_names, new CustomComparer());
            cbo_port_name.Items.AddRange(port_names);
            cbo_port_name.SelectedIndex = cbo_port_name.Items.Count > 0 ? 0 : -1;

            string[] baud_rates = { "110", "300", "600", "1200", "2400", "4800", "9600", "14400", "19200", "38400", "56000", "57600", "115200", "128000", "256000" };
            cbo_port_baud_rate.Items.AddRange(baud_rates);
            cbo_port_baud_rate.Text = "9600";

            string[] paritys = { "None", "Odd", "Even", "Mark", "Space" };
            cbo_port_parity.Items.AddRange(paritys);
            cbo_port_parity.Text = "None";

            string[] data_bits = { "5", "6", "7", "8" };
            cbo_port_data_bits.Items.AddRange(data_bits);
            cbo_port_data_bits.Text = "8";

            string[] stop_bits = { "1", "1.5", "2" };
            cbo_port_stop_bits.Items.AddRange(stop_bits);
            cbo_port_stop_bits.Text = "1";

            //////////////////////////////////////////////////////////////////////////////

            List<string> codepage_list = new List<string>();
            EncodingInfo[] enc_infos = Encoding.GetEncodings();
            string def_codepage = "";
            foreach (EncodingInfo info in enc_infos)
            {
                codepage_list.Add(info.CodePage.ToString() + "," + info.DisplayName);
                if (info.CodePage == Encoding.Default.CodePage)
                {
                    def_codepage = info.CodePage.ToString() + "," + info.DisplayName;
                }
            }
            cbo_gen_code.Items.AddRange(codepage_list.ToArray());
            cbo_gen_code.Text = def_codepage;
            AdjustComboBoxDropDownListWidth(cbo_gen_code);

            chk_recv_show.Checked = true;

            //////////////////////////////////////////////////////////////////////////////

            lua = LuaAPI.NewState();
            lua.L_OpenLibs();

            lua.RegisterGlobalFunc("read", lua_read);
            lua.RegisterGlobalFunc("write", lua_write);
            lua.RegisterGlobalFunc("send", lua_send);
            lua.RegisterGlobalFunc("sleep", lua_sleep);
            lua.RegisterGlobalFunc("show", lua_show);
            lua.RegisterGlobalFunc("help", lua_help);

            //////////////////////////////////////////////////////////////////////////////

            time_timer.Interval = 1;
            time_timer.Start();
            recv_timer.Interval = 10;
            recv_timer.Start();

            serial.DataReceived += serial_DataReceived;
            time_timer.Elapsed += time_timer_ElapsedEventHandler;
            loop_timer.Elapsed += loop_timer_ElapsedEventHandler;
            recv_timer.Elapsed += recv_timer_ElapsedEventHandler;

            cbo_port_name.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
            cbo_port_baud_rate.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
            cbo_port_parity.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
            cbo_port_data_bits.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
            cbo_port_stop_bits.SelectionChangeCommitted += new System.EventHandler(cbo_port_SelectionChangeCommitted);
        }

        private void AdjustComboBoxDropDownListWidth(object comboBox)
        {
            Graphics g = null;
            Font font = null;
            try
            {
                ComboBox senderComboBox = null;
                if (comboBox is ComboBox)
                {
                    senderComboBox = (ComboBox)comboBox;
                }
                else if (comboBox is ToolStripComboBox)
                {
                    senderComboBox = ((ToolStripComboBox)comboBox).ComboBox;
                }
                else
                {
                    return;
                }

                int width = senderComboBox.Width;
                g = senderComboBox.CreateGraphics();
                font = senderComboBox.Font;

                //checks if a scrollbar will be displayed.
                //If yes, then get its width to adjust the size of the drop down list.
                int vertScrollBarWidth =
                    (senderComboBox.Items.Count > senderComboBox.MaxDropDownItems)
                    ? SystemInformation.VerticalScrollBarWidth : 0;

                int newWidth;
                foreach (object s in senderComboBox.Items)  //Loop through list items and check size of each items.
                {
                    if (s != null)
                    {
                        newWidth = (int)g.MeasureString(s.ToString().Trim(), font).Width + vertScrollBarWidth;
                        if (width < newWidth)
                        {
                            width = newWidth;   //set the width of the drop down list to the width of the largest item.
                        }
                    }
                }
                senderComboBox.DropDownWidth = width;
            }
            catch
            {
            }
            finally
            {
                if (g != null)
                {
                    g.Dispose();
                }
            }
        }

        public class CustomComparer : System.Collections.IComparer
        {
            public int Compare(object x, object y)
            {
                string s1 = (string)x;
                string s2 = (string)y;

                if (s1.Length > s2.Length) return 1;
                if (s1.Length < s2.Length) return -1;
                return s1.CompareTo(s2);
            }
        }

        void serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int bytes = serial.BytesToRead;
            int i;

            try
            {
                if (bytes > 0)
                {
                    byte[] buffer = new byte[bytes]; // 声明一个临时数组存储当前来的串口数据
                    serial.Read(buffer, 0, bytes); // 读取缓冲数据

                    for (i = 0; i < bytes; i++)
                    {
                        if (read_buffer.Count() >= 65536)
                        {
                            byte byte_tmp;
                            read_buffer.TryDequeue(out byte_tmp);
                        }
                        read_buffer.Enqueue(buffer[i]);
                    }

                    this.BeginInvoke((EventHandler)(delegate
                    {
                        count_recv += buffer.Length;
                        slab_recv.Text = "接收:" + count_recv.ToString();

                        if (chk_recv_show.Checked)
                        {
                            //serial_recv_data(buffer);
                            serial_recv_buffer.Enqueue(buffer);
                        }
                    }));
                }
            }
            catch (Exception ex)
            {
                this.BeginInvoke((EventHandler)(delegate
                {
                    serial_port_close();

                    MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }));
            }
        }

        void time_timer_ElapsedEventHandler(object sender, EventArgs e)
        {
            if (tn_show_time > 0)
            {
                tn_show_time--;
            }
        }

        void loop_timer_ElapsedEventHandler(object sender, EventArgs e)
        {
            if (System.Threading.Interlocked.Exchange(ref in_loop_timer, 1) == 0)
            {
                serial_send_start();

                System.Threading.Interlocked.Exchange(ref in_loop_timer, 0);
            }
        }

        void recv_timer_ElapsedEventHandler(object sender, EventArgs e)
        {
            if (System.Threading.Interlocked.Exchange(ref in_recv_timer, 1) == 0)
            {
                try
                {
                    List<byte> buffer = new List<byte>();
                    while (buffer.Count < 64 * 1024)
                    {
                        if (serial_recv_buffer.Count == 0)
                        {
                            break;
                        }
                        byte[] buffer_tmp;
                        if (serial_recv_buffer.TryDequeue(out buffer_tmp))
                        {
                            buffer.AddRange(buffer_tmp);
                        }
                    }
                    if (buffer.Count > 0)
                    {
                        this.BeginInvoke((EventHandler)(delegate
                        {
                            serial_recv_data(buffer.ToArray());
                        }));
                    }
                }
                catch (Exception ex)
                {
                    MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }

                System.Threading.Interlocked.Exchange(ref in_recv_timer, 0);
            }
        }

        private void serial_port_open()
        {
            try
            {
                if (serial.IsOpen)
                {
                    serial.Close();
                }

                serial.PortName = cbo_port_name.Text;
                serial.BaudRate = int.Parse(cbo_port_baud_rate.Text);
                serial.Parity = (Parity)Enum.Parse(typeof(Parity), cbo_port_parity.Text);
                serial.DataBits = int.Parse(cbo_port_data_bits.Text);

                StopBits[] stop_bits = { StopBits.One, StopBits.OnePointFive, StopBits.Two };
                serial.StopBits = stop_bits[cbo_port_stop_bits.SelectedIndex];

                serial.Open();
            }
            catch (Exception ex)
            {
                MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

            if (serial.IsOpen)
            {
                btn_port_open.Image = Properties.Resources.open;
                btn_port_open.Text = "     关闭串口";
            }
            else
            {
                btn_port_open.Image = Properties.Resources.close;
                btn_port_open.Text = "     打开串口";
            }
        }

        private void serial_port_close()
        {
            try
            {
                if (loop_timer.Enabled)
                {
                    send_loop_stop();
                }
                if (serial.IsOpen)
                {
                    serial.Close();
                }
            }
            catch
            {

            }

            btn_port_open.Image = Properties.Resources.close;
            btn_port_open.Text = "     打开串口";
        }

        private void btn_port_open_Click(object sender, EventArgs e)
        {
            if (serial.IsOpen)
            {
                serial_port_close();
            }
            else
            {
                serial_port_open();
            }
        }

        private void cbo_port_name_DropDown(object sender, EventArgs e)
        {
            string port_name_old = cbo_port_name.Text;

            string[] port_names = SerialPort.GetPortNames();
            Array.Sort(port_names, new CustomComparer());
            cbo_port_name.Items.Clear();
            cbo_port_name.Items.AddRange(port_names);

            cbo_port_name.Text = port_name_old;

            if ((cbo_port_name.Items.Count > 0) && (cbo_port_name.SelectedIndex < 0))
            {
                cbo_port_name.SelectedIndex = 0;
            }
        }

        private void cbo_port_SelectionChangeCommitted(object sender, EventArgs e)
        {
            if (serial.IsOpen)
            {
                serial_port_open();
            }
        }

        private void txt_show_send_KeyDown(object sender, KeyEventArgs e)
        {
            if ((e.KeyCode == Keys.Enter) && (e.Modifiers == Keys.Control))
            {
                serial_send_start();
                e.SuppressKeyPress = true;
            }
        }

        private void txt_show_recv_KeyDown(object sender, KeyEventArgs e)
        {
            if (chk_mode_terminal.Checked)
            {
                if (serial.IsOpen)
                {
                    if (e.KeyCode == Keys.Up)
                    {
                        byte[] bytes = { 0x1B, 0x5B, 0x41 };
                        serial_send_data(bytes);
                        e.Handled = true;
                    }
                    else if (e.KeyCode == Keys.Down)
                    {
                        byte[] bytes = { 0x1B, 0x5B, 0x42 };
                        serial_send_data(bytes);
                        e.Handled = true;
                    }
                    else if (e.KeyCode == Keys.Right)
                    {
                        byte[] bytes = { 0x1B, 0x5B, 0x43 };
                        serial_send_data(bytes);
                        e.Handled = true;
                    }
                    else if (e.KeyCode == Keys.Left)
                    {
                        byte[] bytes = { 0x1B, 0x5B, 0x44 };
                        serial_send_data(bytes);
                        e.Handled = true;
                    }
                    else if (e.KeyCode == Keys.Escape)
                    {
                        byte[] bytes = { 0x1B };
                        serial_send_data(bytes);
                        e.Handled = true;
                    }
                    else if (e.KeyCode == Keys.Delete)
                    {
                        byte[] bytes = { 0x7F };
                        serial_send_data(bytes);
                        e.Handled = true;
                    }
                }
            }
        }

        private void txt_show_recv_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (chk_mode_terminal.Checked)
            {
                if (serial.IsOpen)
                {
                    serial_send_data(new byte[] { (byte)e.KeyChar });
                }

                e.Handled = true;
            }
        }

        private void txt_show_send_clear()
        {

        }

        private void lbl_send_clear_Click(object sender, EventArgs e)
        {
            if (chk_send_file.Checked)
            {
                chk_send_file.Checked = false;
            }
            else
            {
                txt_show_send.Clear();
            }
        }

        private void txt_show_recv_clear()
        {
            if (chk_recv_file.Checked)
            {
                chk_recv_file.Checked = false;
            }
            else
            {
                txt_show_recv.Clear();
            }

            count_recv = 0;
            slab_recv.Text = "接收:" + count_recv.ToString();
            count_send = 0;
            slab_send.Text = "发送:" + count_send.ToString();
        }

        private void lbl_recv_clear_Click(object sender, EventArgs e)
        {
            txt_show_recv_clear();
        }

        private void chk_send_hex_CheckedChanged(object sender, EventArgs e)
        {
            try
            {
                chk_esc_char.Enabled = !chk_send_hex.Checked;

                if (!chk_send_file.Checked)
                {
                    if (!chk_send_lua.Checked)
                    {
                        if (chk_send_hex.Checked)
                        {
                            txt_show_send.Text = byte_to_hex(encoder.GetBytes(txt_show_send.Text));
                        }
                        else
                        {
                            txt_show_send.Text = encoder.GetString(hex_to_byte(txt_show_send.Text));
                        }
                    }
                }
            }
            catch (Exception ee)
            {
                MessageBoxEx.Show(this, ee.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private byte[] calc_modbus_crc(byte[] buffer)
        {
            UInt16 crc = 0xFFFF;
            int i, j;

            for (i = 0; i < buffer.Length; i++)
            {
                crc ^= buffer[i];
                for (j = 0; j < 8; j++)
                {
                    if ((crc & 0x0001) != 0)
                    {
                        crc >>= 1;
                        crc ^= 0xA001;
                    }
                    else
                    {
                        crc >>= 1;
                    }
                }
            }

            return BitConverter.GetBytes(crc);
        }

        private byte[] calc_crc_16(UInt16 polynomials, byte[] buffer)
        {
            UInt16 crc = 0x0000;
            int i, j;

            for (i = 0; i < buffer.Length; i++)
            {
                crc ^= (UInt16)((UInt16)buffer[i] << 8);
                for (j = 0; j < 8; j++)
                {
                    if ((crc & 0x8000) != 0)
                    {
                        crc <<= 1;
                        crc ^= polynomials;
                    }
                    else
                    {
                        crc <<= 1;
                    }
                }
            }

            return BitConverter.GetBytes(crc);
        }

        private byte[] calc_sum_16(byte[] buffer)
        {
            UInt16 sum = 0;
            int i;
            for (i = 0; i < buffer.Length; i++)
            {
                sum += buffer[i];
            }

            return BitConverter.GetBytes(sum);
        }

        private byte[] calc_xor(byte[] buffer)
        {
            byte xor = 0;
            int i;
            for (i = 0; i < buffer.Length; i++)
            {
                xor ^= buffer[i];
            }

            return new byte[] { xor };
        }

        private byte[] hex_to_byte(string str_hex)
        {
            List<byte> buffer = new List<byte>();
            int len;

            str_hex = str_hex.Trim();
            while (str_hex.Length > 0)
            {
                len = str_hex.Length;
                if (len > 2) len = 2;
                buffer.Add(byte.Parse(str_hex.Substring(0, len), System.Globalization.NumberStyles.HexNumber));
                str_hex = str_hex.Substring(len).TrimStart();
            }

            return buffer.ToArray();
        }

        private string byte_to_hex(byte[] buffer)
        {
            int i;
            StringBuilder str_tmp = new StringBuilder();

            for (i = 0; i < buffer.Length; i++)
            {
                str_tmp.Append(buffer[i].ToString("X2") + " ");
            }

            return str_tmp.ToString();
        }

        private void serial_recv_data(byte[] buffer)
        {
            string str_tmp = "";

            if (chk_recv_time.Checked)
            {
                if (tn_show_time == 0)
                {
                    if (txt_show_recv.Text.Length > 0)
                    {
                        str_tmp += "\r\n";
                    }
                    str_tmp += "【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "】";
                }

                tn_show_time = 10;
            }
            if (chk_recv_hex.Checked)
            {
                str_tmp += byte_to_hex(buffer);
            }
            else
            {
                str_tmp += encoder.GetString(buffer);
            }

            if (_recv_file)
            {
                File.AppendAllText(recv_file_path, str_tmp);
            }
            else
            {
                string[] strs = str_tmp.Split('\b');
                int i;

                for (i = 0; i < strs.Count(); i++)
                {
                    if ((txt_show_recv.TextLength >= 1) && (i > 0))
                    {
                        string str_select = "";
                        txt_show_recv.Select(txt_show_recv.TextLength - 1, 1);
                        if (txt_show_recv.SelectedText[0] == '\n')
                        {
                            if ((txt_show_recv.TextLength >= 2) && (txt_show_recv.Text[txt_show_recv.TextLength - 2] == '\r'))
                            {
                                txt_show_recv.Select(txt_show_recv.TextLength - 2, 2);
                            }
                        }
                        else
                        {
                            byte[] byte_tmp = encoder.GetBytes(txt_show_recv.SelectedText);
                            if (byte_tmp.Length > 1)
                            {
                                byte[] byte_select = new byte[byte_tmp.Length - 1];
                                Array.Copy(byte_tmp, byte_select, byte_select.Length);
                                str_select = encoder.GetString(byte_select);
                            }
                        }
                        txt_show_recv.SelectedText = str_select;
                    }

                    if (strs[i].LastIndexOf("\x1B[2K") >= 0)
                    {
                        strs[i] = strs[i].Substring(strs[i].LastIndexOf("\x1B[2K") + "\x1B[2K".Length);

                        int pos = txt_show_recv.Text.LastIndexOf("\r\n");
                        if (pos >= 0)
                        {
                            pos += "\r\n".Length;
                            txt_show_recv.Select(pos, txt_show_recv.Text.Length - pos);
                            txt_show_recv.SelectedText = "";
                        }
                        else
                        {
                            txt_show_recv.Text = "";
                        }
                    }

                    txt_show_recv.AppendText(strs[i]);

                    /*
                     * The color for terminal (foreground)
                     * BLACK    30
                     * RED      31
                     * GREEN    32
                     * YELLOW   33
                     * BLUE     34
                     * PURPLE   35
                     * CYAN     36
                     * WHITE    37
                     */
                }
            }
        }

        private void serial_send_data(byte[] buffer)
        {
            if (buffer.Length > 0)
            {
                if (serial.IsOpen)
                {
                    lock (serial)
                    {
                        serial.Write(buffer, 0, buffer.Length);
                    }

                    this.BeginInvoke((EventHandler)(delegate
                    {
                        count_send += buffer.Length;
                        slab_send.Text = "发送:" + count_send.ToString();

                        if (chk_send_show.Checked)
                        {
                            //serial_recv_data(buffer);
                            serial_recv_buffer.Enqueue(buffer);
                        }
                    }));
                }
                else
                {
                    this.BeginInvoke((EventHandler)(delegate
                    {
                        //serial_recv_data(buffer);
                        serial_recv_buffer.Enqueue(buffer);
                    }));
                }
            }
        }

        private void send_file_thread()
        {
            FileStream file_reader = new FileStream(send_file_path, FileMode.Open, FileAccess.Read);
            long length = file_reader.Length;
            long offset = 0, count = 0;

            try
            {
                while ((!_file_should_stop) && (offset < length))
                {
                    count = length - offset;
                    if (count > 32)
                    {
                        count = 32;
                    }

                    byte[] buffer = new byte[count];
                    file_reader.Read(buffer, 0, (int)count);

                    if (chk_send_hex.Checked)
                    {
                        buffer = hex_to_byte(encoder.GetString(buffer));
                    }

                    serial_send_data(buffer);

                    offset += count;
                }
            }
            catch (Exception ex)
            {
                this.BeginInvoke((EventHandler)(delegate
                {
                    serial_send_stop();

                    MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }));
            }
            finally
            {
                file_reader.Close();

                file_thread = null;

                this.BeginInvoke((EventHandler)(delegate
                {
                    if (!chk_pack_loop.Checked)
                    {
                        btn_show_send.Text = "发送";
                    }
                }));
            }
        }

        int number_parse(string str, int len, int radio, out int num)
        {
            string str_hex = "0123456789ABCDEF";
            int i;

            num = 0;
            if (len > str.Length)
            {
                len = str.Length;
            }
            for (i = 0; i < len; i++)
            {
                int n = str_hex.IndexOf(Char.ToUpper(str[i]));
                if ((n < 0) || (n >= radio))
                {
                    break;
                }
                num *= radio;
                num += n;
            }

            return i;
        }

        string[] string_split(string input, string pattern)
        {
            List<string> list = new List<string>();
            int start = 0, index;
            while ((index = input.IndexOf(pattern, start)) >= 0)
            {
                list.Add(input.Substring(start, index - start));
                index += pattern.Length;
                start = index;
            }
            list.Add(input.Substring(start));

            return list.ToArray();
        }

        byte[] string_escape(string text)
        {
            List<byte> list = new List<byte>();
            string[] strs = string_split(text, "\\\\");
            string esc_string = "abfnrtv";
            string esc_value = "\a\b\f\n\r\t\v";

            for (int i = 0; i < strs.Length; i++)
            {
                if (i > 0)
                {
                    list.AddRange(encoder.GetBytes("\\"));
                }

                if (strs[i].Length > 0)
                {
                    string[] strc = strs[i].Split('\\');
                    list.AddRange(encoder.GetBytes(strc[0]));
                    for (int j = 1; j < strc.Length; j++)
                    {
                        int index = esc_string.IndexOf(strc[j][0]);
                        if (index >= 0)
                        {
                            list.AddRange(encoder.GetBytes(esc_value[index].ToString()));
                            list.AddRange(encoder.GetBytes(strc[j].Substring(1)));
                        }
                        else if (strc[j][0] == 'x')
                        {
                            int num;
                            int len = number_parse(strc[j].Substring(1), 2, 16, out num);
                            if (len <= 0)
                            {
                                throw new ArgumentException("无法解析的数值");
                            }
                            list.Add((byte)num);
                            list.AddRange(encoder.GetBytes(strc[j].Substring(1 + len)));
                        }
                        else if ((strc[j][0] >= '0') && (strc[j][0] <= '7'))
                        {
                            int num;
                            int len = number_parse(strc[j], 3, 8, out num);
                            if (len <= 0)
                            {
                                throw new ArgumentException("无法解析的数值");
                            }
                            list.Add((byte)num);
                            list.AddRange(encoder.GetBytes(strc[j].Substring(len)));
                        }
                        else
                        {
                            list.AddRange(encoder.GetBytes(strc[j]));
                        }
                    }
                }
            }

            return list.ToArray();
        }

        private void serial_send_text(string text)
        {
            byte[] buffer_tmp;
            List<byte> buffer = new List<byte>();

            try
            {
                if (chk_pack_head.Checked)
                {
                    if (chk_send_hex.Checked)
                    {
                        buffer.AddRange(hex_to_byte(txt_pack_head.Text));
                    }
                    else
                    {
                        if (chk_esc_char.Checked)
                        {
                            buffer.AddRange(string_escape(txt_pack_head.Text));
                        }
                        else
                        {
                            buffer.AddRange(encoder.GetBytes(txt_pack_head.Text));
                        }
                    }
                }

                if (chk_send_hex.Checked)
                {
                    buffer_tmp = hex_to_byte(text);
                }
                else
                {
                    if (chk_esc_char.Checked)
                    {
                        buffer_tmp = string_escape(text);
                    }
                    else
                    {
                        buffer_tmp = encoder.GetBytes(text);
                    }
                }
                buffer.AddRange(buffer_tmp);

                if (chk_pack_check.Checked)
                {
                    byte[] check_buffer;
                    byte byte_tmp;
                    int check_type = 0;
                    this.Invoke((EventHandler)(delegate
                    {
                        check_type = cbo_pack_check.SelectedIndex;
                    }));
                    switch (check_type)
                    {
                    case 0: // XOR异或校验 
                        check_buffer = calc_xor(buffer_tmp);
                        buffer.AddRange(check_buffer);
                        break;
                    case 1: // SUM8校验
                        check_buffer = calc_sum_16(buffer_tmp);
                        buffer.Add(check_buffer[0]);
                        break;
                    case 2: // ModBusLRC校验
                        check_buffer = calc_sum_16(buffer_tmp);
                        buffer.AddRange(new byte[] { (byte)(-check_buffer[0]) });
                        break;
                    case 3: // CRC16低字节在前
                        check_buffer = calc_crc_16(0x1021, buffer_tmp);
                        buffer.AddRange(check_buffer);
                        break;
                    case 4: // CRC16高字节在前
                        check_buffer = calc_crc_16(0x1021, buffer_tmp);
                        byte_tmp = check_buffer[0];
                        check_buffer[0] = check_buffer[1];
                        check_buffer[1] = byte_tmp;
                        buffer.AddRange(check_buffer);
                        break;
                    case 5: // SUM16校验和低字节在前
                        check_buffer = calc_sum_16(buffer_tmp);
                        buffer.AddRange(check_buffer);
                        break;
                    case 6: // SUM16校验和高字节在前
                        check_buffer = calc_sum_16(buffer_tmp);
                        byte_tmp = check_buffer[0];
                        check_buffer[0] = check_buffer[1];
                        check_buffer[1] = byte_tmp;
                        buffer.AddRange(check_buffer);
                        break;
                    case 7: // ModBusCRC低字节在前
                        check_buffer = calc_modbus_crc(buffer_tmp);
                        buffer.AddRange(check_buffer);
                        break;
                    case 8: // ModBusCRC高字节在前
                        check_buffer = calc_modbus_crc(buffer_tmp);
                        byte_tmp = check_buffer[0];
                        check_buffer[0] = check_buffer[1];
                        check_buffer[1] = byte_tmp;
                        buffer.AddRange(check_buffer);
                        break;
                    }
                }

                if (chk_pack_end.Checked)
                {
                    if (chk_send_hex.Checked)
                    {
                        buffer.AddRange(hex_to_byte(txt_pack_end.Text));
                    }
                    else
                    {
                        if (chk_esc_char.Checked)
                        {
                            buffer.AddRange(string_escape(txt_pack_end.Text));
                        }
                        else
                        {
                            buffer.AddRange(encoder.GetBytes(txt_pack_end.Text));
                        }
                    }
                }

                serial_send_data(buffer.ToArray());
            }
            catch (Exception ex)
            {
                this.BeginInvoke((EventHandler)(delegate
                {
                    group_send_stop();
                    serial_send_stop();

                    MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }));
            }
        }

        private int lua_read(ILuaState state)
        {
            if (read_buffer.Count() > 0)
            {
                state.PushBoolean(true);
                byte byte_tmp = 0;
                read_buffer.TryDequeue(out byte_tmp);
                state.PushInteger(byte_tmp);
            }
            else
            {
                state.PushBoolean(false);
                state.PushInteger(0);
            }

            return 2;
        }

        private int lua_write(ILuaState state)
        {
            int n_tmp = state.L_CheckInteger(1);
            byte[] buffer = { (byte)n_tmp };

            try
            {
                serial_send_data(buffer);
            }
            catch (Exception ex)
            {
                this.BeginInvoke((EventHandler)(delegate
                {
                    serial_send_stop();

                    MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }));
            }

            return 0;
        }

        private int lua_send(ILuaState state)
        {
            string text = state.L_CheckString(1);

            serial_send_text(text);

            return 0;
        }

        private int lua_sleep(ILuaState state)
        {
            int timeout = state.L_CheckInteger(1);

            System.Threading.Thread.Sleep(timeout);

            return 0;
        }

        private int lua_show(ILuaState state)
        {
            string text = state.L_CheckString(1);

            this.Invoke((EventHandler)(delegate
            {
                txt_show_recv.AppendText(text);
            }));

            return 0;
        }

        private int lua_help(ILuaState state)
        {
            this.Invoke((EventHandler)(delegate
            {
                txt_show_recv.AppendText("///////////////////////////////////////////////" + "\r\n");
                txt_show_recv.AppendText("valid,value read(void);" + "\r\n");
                txt_show_recv.AppendText("void write(int value);" + "\r\n");
                txt_show_recv.AppendText("void send(string text);" + "\r\n");
                txt_show_recv.AppendText("void sleep(int timeout);" + "\r\n");
                txt_show_recv.AppendText("void show(string text);" + "\r\n");
                txt_show_recv.AppendText("void help(void);" + "\r\n");
                txt_show_recv.AppendText("///////////////////////////////////////////////" + "\r\n");
            }));

            return 0;
        }

        private void send_lua_thread(object obj)
        {
            string text = (string)obj;

            try
            {
                var status = lua.L_DoString(text);
                if (status != ThreadStatus.LUA_OK)
                {
                    throw new Exception(lua.ToString(-1));
                }
            }
            catch (Exception ex)
            {
                if (!_lua_should_stop)
                {
                    this.BeginInvoke((EventHandler)(delegate
                    {
                        serial_send_stop();

                        MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    }));
                }
            }
            finally
            {
                lua_thread = null;

                if (!_lua_should_stop)
                {
                    this.BeginInvoke((EventHandler)(delegate
                    {
                        if (!chk_pack_loop.Checked)
                        {
                            btn_show_send.Text = "发送";
                        }
                    }));
                }
            }
        }

        private void serial_send_start()
        {
            try
            {
                if (chk_send_lua.Checked)
                {
                    if (lua_thread == null)
                    {
                        lua_thread = new System.Threading.Thread(send_lua_thread);
                        _lua_should_stop = false;
                        lua_thread.Start(txt_show_send.Text);

                        if (!chk_pack_loop.Checked)
                        {
                            btn_show_send.Text = "停止";
                        }
                    }
                }
                else if (chk_send_file.Checked)
                {
                    if (file_thread == null)
                    {
                        file_thread = new System.Threading.Thread(send_file_thread);
                        _file_should_stop = false;
                        file_thread.Start();

                        if (!chk_pack_loop.Checked)
                        {
                            btn_show_send.Text = "停止";
                        }
                    }
                }
                else
                {
                    serial_send_text(txt_show_send.Text);
                }
            }
            catch (Exception ex)
            {
                serial_send_stop();

                MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        private void send_loop_start()
        {
            loop_timer.Enabled = true;
            btn_show_send.Text = "停止";
        }

        private void send_loop_stop()
        {
            if (lua_thread != null)
            {
                _lua_should_stop = true;
                lua_thread.Abort();
                while (lua_thread != null)
                {
                    Application.DoEvents();
                }
                btn_show_send.Text = "发送";
            }
            else if (file_thread != null)
            {
                while (file_thread != null)
                {
                    _file_should_stop = true;
                    Application.DoEvents();
                }
            }

            loop_timer.Enabled = false;
            btn_show_send.Text = "发送";
        }

        private bool serial_send_stop()
        {
            if (loop_timer.Enabled)
            {
                send_loop_stop();

                return true;
            }
            else if (lua_thread != null)
            {
                _lua_should_stop = true;
                lua_thread.Abort();
                while (lua_thread != null)
                {
                    Application.DoEvents();
                }
                btn_show_send.Text = "发送";

                return true;
            }
            else if (file_thread != null)
            {
                while (file_thread != null)
                {
                    _file_should_stop = true;
                    Application.DoEvents();
                }

                return true;
            }

            byte byte_tmp;
            while (read_buffer.TryDequeue(out byte_tmp))
            {

            }

            return false;
        }

        private void btn_show_send_Click(object sender, EventArgs e)
        {
            if (!serial_send_stop())
            {
                if (chk_pack_loop.Checked)
                {
                    send_loop_start();
                }

                serial_send_start();
            }
        }

        private void chk_pack_loop_CheckedChanged(object sender, EventArgs e)
        {
            if (chk_pack_loop.Checked)
            {
                num_pack_loop.Enabled = false;
                loop_timer.Interval = (int)num_pack_loop.Value;
            }
            else
            {
                send_loop_stop();
                num_pack_loop.Enabled = true;
            }
        }

        private void chk_recv_file_CheckedChanged(object sender, EventArgs e)
        {
            if (chk_recv_file.Checked)
            {
                SaveFileDialog fileDialog = new SaveFileDialog();

                fileDialog.Title = "接收转向文件...";
                fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
                if (fileDialog.ShowDialog() == DialogResult.OK)
                {
                    recv_file_path = fileDialog.FileName;
                }
                else
                {
                    chk_recv_file.Checked = false;
                }
            }

            if (chk_recv_file.Checked)
            {
                txt_show_recv.Text = "文件路径: \"" + recv_file_path + "\"";
                txt_show_recv.BackColor = System.Drawing.SystemColors.Control;
            }
            else
            {
                txt_show_recv.Clear();
                txt_show_recv.BackColor = System.Drawing.SystemColors.Window;
            }

            _recv_file = chk_recv_file.Checked;
        }

        private void chk_send_file_CheckedChanged(object sender, EventArgs e)
        {
            if (chk_send_file.Checked)
            {
                OpenFileDialog fileDialog = new OpenFileDialog();

                fileDialog.Title = "发送文件数据...";
                fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
                if (fileDialog.ShowDialog() == DialogResult.OK)
                {
                    send_file_path = fileDialog.FileName;
                }
                else
                {
                    chk_send_file.Checked = false;
                }
            }

            if (chk_send_file.Checked)
            {
                txt_show_send.Text = "文件路径: \"" + send_file_path + "\"\r\n" + "文件长度: " + new FileInfo(send_file_path).Length.ToString() + "字节";
                txt_show_send.ReadOnly = true;

                chk_send_lua.Enabled = false;
                chk_send_lua.Checked = false;
            }
            else
            {
                chk_send_lua.Enabled = true;

                txt_show_send.Clear();
                txt_show_send.ReadOnly = false;

                if (file_thread != null)
                {
                    while (file_thread != null)
                    {
                        _file_should_stop = true;
                        Application.DoEvents();
                    }
                }
            }
        }

        private void slab_info_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://www.mysoow.com");  // 在浏览器中打开链接
        }

        private void lbl_recv_save_Click(object sender, EventArgs e)
        {
            SaveFileDialog fileDialog = new SaveFileDialog();

            fileDialog.Title = "保存数据";
            fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                File.WriteAllText(fileDialog.FileName, txt_show_recv.Text);
            }
        }

        private void lbl_send_load_Click(object sender, EventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();

            fileDialog.Title = "载入数据";
            fileDialog.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                chk_send_file.Checked = false;
                txt_show_send.Text = File.ReadAllText(fileDialog.FileName, TxtFileEncoder.GetEncoding(fileDialog.FileName));
            }
        }

        private void config_load()
        {
            config.Reload();

            cbo_port_name.Text = config.cbo_port_name;
            cbo_port_baud_rate.Text = config.cbo_port_baud_rate;
            cbo_port_parity.Text = config.cbo_port_parity;
            cbo_port_data_bits.Text = config.cbo_port_data_bits;
            cbo_port_stop_bits.Text = config.cbo_port_stop_bits;

            cbo_gen_code.Text = config.cbo_gen_code;

            chk_recv_time.Checked = config.chk_recv_time;
            chk_recv_hex.Checked = config.chk_recv_hex;
            chk_send_show.Checked = config.chk_send_show;
            chk_recv_show.Checked = config.chk_recv_show;

            chk_send_hex.Checked = config.chk_send_hex;
            chk_send_lua.Checked = config.chk_send_lua;
            chk_mode_terminal.Checked = config.chk_mode_terminal;
            chk_esc_char.Checked = config.chk_esc_char;

            chk_pack_head.Checked = config.chk_pack_head;
            txt_pack_head.Text = config.txt_pack_head;
            chk_pack_check.Checked = config.chk_pack_check;
            cbo_pack_check.Text = config.cbo_pack_check;
            chk_pack_end.Checked = config.chk_pack_end;
            txt_pack_end.Text = config.txt_pack_end;
            num_pack_loop.Value = config.num_pack_loop;
            chk_pack_loop.Checked = config.chk_pack_loop;

            spc_back1.Panel1Collapsed = config.chk_show_left;
            spc_show.Panel2Collapsed = config.chk_show_down;
            spc_back0.Panel2Collapsed = config.chk_show_right;

            txt_show_send.Text = config.txt_show_send;

            txt_group_send.Text = config.txt_group_send;
            num_group_delay.Value = config.num_group_delay;
            txt_group_note.Text = config.txt_group_note;

            DeserializeListViewItems(list_group_list, config.list_group_list);
        }

        private void config_save()
        {
            config.cbo_port_name = cbo_port_name.Text;
            config.cbo_port_baud_rate = cbo_port_baud_rate.Text;
            config.cbo_port_parity = cbo_port_parity.Text;
            config.cbo_port_data_bits = cbo_port_data_bits.Text;
            config.cbo_port_stop_bits = cbo_port_stop_bits.Text;

            config.cbo_gen_code = cbo_gen_code.Text;

            config.chk_recv_time = chk_recv_time.Checked;
            config.chk_recv_hex = chk_recv_hex.Checked;
            config.chk_send_show = chk_send_show.Checked;
            config.chk_recv_show = chk_recv_show.Checked;

            config.chk_send_hex = chk_send_hex.Checked;
            config.chk_send_lua = chk_send_lua.Checked;
            config.chk_mode_terminal = chk_mode_terminal.Checked;
            config.chk_esc_char = chk_esc_char.Checked;

            config.chk_pack_head = chk_pack_head.Checked;
            config.txt_pack_head = txt_pack_head.Text;
            config.chk_pack_check = chk_pack_check.Checked;
            config.cbo_pack_check = cbo_pack_check.Text;
            config.chk_pack_end = chk_pack_end.Checked;
            config.txt_pack_end = txt_pack_end.Text;
            config.chk_pack_loop = chk_pack_loop.Checked;
            config.num_pack_loop = num_pack_loop.Value;

            config.chk_show_left = spc_back1.Panel1Collapsed;
            config.chk_show_down = spc_show.Panel2Collapsed;
            config.chk_show_right = spc_back0.Panel2Collapsed;

            config.txt_show_send = txt_show_send.Text;

            config.txt_group_send = txt_group_send.Text;
            config.num_group_delay = num_group_delay.Value;
            config.txt_group_note = txt_group_note.Text;

            config.list_group_list = SerializeListViewItems(list_group_list);

            config.Save();
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            config_load();
        }

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            group_send_stop();
            serial_send_stop();
            config_save();
        }

        private void CopyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (txt_show_recv.SelectionLength > 0)
            {
                Clipboard.SetText(txt_show_recv.SelectedText);
            }
        }

        private void PasteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (serial.IsOpen)
            {
                serial_send_data(encoder.GetBytes(Clipboard.GetText()));
            }
        }

        private void SelectAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            txt_show_recv.SelectAll();
        }

        private void ClearToolStripMenuItem_Click(object sender, EventArgs e)
        {
            txt_show_recv_clear();
        }

        private void menu_show_recv_Opened(object sender, EventArgs e)
        {
            CopyToolStripMenuItem.Enabled = txt_show_recv.SelectionLength > 0;
            PasteToolStripMenuItem.Enabled = Clipboard.ContainsText();
            SelectAllToolStripMenuItem.Enabled = txt_show_recv.SelectionLength < txt_show_recv.Text.Length;
            ClearToolStripMenuItem.Enabled = txt_show_recv.TextLength > 0;
        }

        private void lbl_hide_left_Click(object sender, EventArgs e)
        {
            spc_back1.Panel1Collapsed = !spc_back1.Panel1Collapsed;
        }

        private void lbl_hide_left_MouseEnter(object sender, EventArgs e)
        {
            lbl_hide_left.Image = spc_back1.Panel1Collapsed ? Properties.Resources.right : Properties.Resources.left;
        }

        private void lbl_hide_left_MouseLeave(object sender, EventArgs e)
        {
            lbl_hide_left.Image = null;
        }

        private void lbl_hide_down_Click(object sender, EventArgs e)
        {
            spc_show.Panel2Collapsed = !spc_show.Panel2Collapsed;
        }

        private void lbl_hide_down_MouseEnter(object sender, EventArgs e)
        {
            lbl_hide_down.Image = spc_show.Panel2Collapsed ? Properties.Resources.up : Properties.Resources.down;
        }

        private void lbl_hide_down_MouseLeave(object sender, EventArgs e)
        {
            lbl_hide_down.Image = null;
        }

        private void lbl_hide_right_Click(object sender, EventArgs e)
        {
            spc_back0.Panel2Collapsed = !spc_back0.Panel2Collapsed;
        }

        private void lbl_hide_right_MouseEnter(object sender, EventArgs e)
        {
            lbl_hide_right.Image = spc_back0.Panel2Collapsed ? Properties.Resources.left : Properties.Resources.right;
        }

        private void lbl_hide_right_MouseLeave(object sender, EventArgs e)
        {
            lbl_hide_right.Image = null;
        }

        void listIndexUpdate()
        {
            list_group_list.BeginUpdate();
            for (int i = 0; i < list_group_list.Items.Count; i++)
            {
                list_group_list.Items[i].Text = (i + 1).ToString();
            }
            list_group_list.EndUpdate();
        }

        private void list_group_list_KeyDown(object sender, KeyEventArgs e)
        {
            list_group_list.Tag = 1;

            if (e.KeyCode == Keys.Enter)
            {
                if (list_group_list.SelectedItems.Count > 0)
                {
                    serial_send_text(list_group_list.SelectedItems[0].SubItems[1].Text);
                }
            }
            else if (e.Control)
            {
                if ((e.KeyCode == Keys.Up) || (e.KeyCode == Keys.Down))
                {
                    if (list_group_list.SelectedItems.Count > 0)
                    {
                        ListViewItem lvi = list_group_list.SelectedItems[0];
                        int index = lvi.Index;
                        if (((e.KeyCode == Keys.Up) && (index > 0)) ||
                            ((e.KeyCode == Keys.Down) && (index < (list_group_list.Items.Count - 1))))
                        {
                            lvi.Remove();
                            list_group_list.Items.Insert((e.KeyCode == Keys.Up) ? (index - 1) : (index + 1), lvi);
                            lvi.Focused = true;
                            listIndexUpdate();
                        }
                    }
                    e.Handled = true;
                }
            }
        }

        private void list_group_list_MouseDown(object sender, MouseEventArgs e)
        {
            list_group_list.Tag = 0;
        }

        private void list_group_list_Click(object sender, EventArgs e)
        {
            list_group_list.Tag = (int)list_group_list.Tag + 1;
        }

        private void list_group_list_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if ((int)list_group_list.Tag != 1)
            {
                e.NewValue = e.CurrentValue;
            }
            list_group_list.Tag = (int)list_group_list.Tag + 10;
        }

        private void list_group_list_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (((int)list_group_list.Tag == 10) && (list_group_list.SelectedItems.Count > 0))
            {
                ListViewHitTestInfo lvi = list_group_list.HitTest(e.Location);
                if ((lvi != null) && (lvi.Item == list_group_list.SelectedItems[0]))
                {
                    group_send_text_try(lvi.Item.SubItems[1].Text);
                }
            }
        }

        private void list_group_list_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            if (e.IsSelected)
            {
                txt_group_send.Text = e.Item.SubItems[1].Text;
                num_group_delay.Value = Decimal.Parse(e.Item.SubItems[2].Text);
                txt_group_note.Text = e.Item.SubItems[3].Text;
            }

            btn_group_delete.Enabled = e.IsSelected;
            btn_group_modify.Enabled = e.IsSelected;
        }

        private void btn_group_append_Click(object sender, EventArgs e)
        {
            if (txt_group_send.Text.Length == 0)
            {
                MessageBoxEx.Show(this, "请填写数据", "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            list_group_list.Items.Add(new ListViewItem(new string[] { (list_group_list.Items.Count + 1).ToString(),
                txt_group_send.Text, num_group_delay.Value.ToString(), txt_group_note.Text }));
        }

        private void btn_group_delete_Click(object sender, EventArgs e)
        {
            list_group_list.BeginUpdate();
            if (list_group_list.SelectedItems.Count > 0)
            {
                int index = list_group_list.SelectedItems[0].Index;
                list_group_list.SelectedItems[0].Remove();
                if (list_group_list.Items.Count > 0)
                {
                    if (index >= list_group_list.Items.Count)
                    {
                        index--;
                    }
                    list_group_list.Items[index].Selected = true;
                }

                listIndexUpdate();
            }
            list_group_list.EndUpdate();
        }

        private void btn_group_modify_Click(object sender, EventArgs e)
        {
            if (list_group_list.SelectedItems.Count > 0)
            {
                if (txt_group_send.Text.Length == 0)
                {
                    MessageBoxEx.Show(this, "请填写数据", "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }

                ListViewItem lvi = list_group_list.SelectedItems[0];
                lvi.SubItems[1].Text = txt_group_send.Text;
                lvi.SubItems[2].Text = num_group_delay.Value.ToString();
                lvi.SubItems[3].Text = txt_group_note.Text;
            }
        }

        private void list_group_list_ItemDrag(object sender, ItemDragEventArgs e)
        {
            list_group_list.DoDragDrop(e.Item, DragDropEffects.Move);
        }

        private void list_group_list_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Move;
        }

        private void list_group_list_DragOver(object sender, DragEventArgs e)
        {
            Point ptScreen = new Point(e.X, e.Y);
            Point pt = list_group_list.PointToClient(ptScreen);
            ListViewItem item = list_group_list.GetItemAt(pt.X, pt.Y);
            if (item != null)
            {
                item.Selected = true;
            }
        }

        private void list_group_list_DragDrop(object sender, DragEventArgs e)
        {
            ListViewItem draggedItem = (ListViewItem)e.Data.GetData(typeof(ListViewItem));
            Point ptScreen = new Point(e.X, e.Y);
            Point pt = list_group_list.PointToClient(ptScreen);
            ListViewItem targetItem = list_group_list.GetItemAt(pt.X, pt.Y);
            if (targetItem == null)
            {
                return;
            }

            if (targetItem.Index < draggedItem.Index)
            {
                targetItem = list_group_list.Items.Insert(targetItem.Index, (ListViewItem)draggedItem.Clone());
            }
            else
            {
                targetItem = list_group_list.Items.Insert(targetItem.Index + 1, (ListViewItem)draggedItem.Clone());
            }

            list_group_list.Tag = 1;
            targetItem.Checked = draggedItem.Checked;
            list_group_list.Items.Remove(draggedItem);
            targetItem.Selected = true;
            targetItem.Focused = true;

            listIndexUpdate();
        }

        public string SerializeListViewItems(ListView listView)
        {
            StringBuilder items = new StringBuilder();
            BinaryFormatter serializer = new BinaryFormatter();

            for (int i = 0; i < listView.Items.Count; i++)
            {
                MemoryStream ms = new MemoryStream();
                serializer.Serialize(ms, listView.Items[i]);

                if (i > 0) items.Append(",");
                items.Append(Convert.ToBase64String(ms.ToArray()));
            }

            return items.ToString();
        }

        public bool DeserializeListViewItems(ListView listView, string items)
        {
            try
            {
                if (items.Length > 0)
                {
                    string[] strs = items.Split(',');
                    BinaryFormatter serializer = new BinaryFormatter();

                    listView.Items.Clear();
                    for (int i = 0; i < strs.Length; i++)
                    {
                        MemoryStream ms = new MemoryStream(Convert.FromBase64String(strs[i]));

                        ListViewItem lvi = serializer.Deserialize(ms) as ListViewItem;
                        ListViewItem item = new ListViewItem(lvi.Text);
                        for (int j = 1; j < lvi.SubItems.Count; j++)
                        {
                            item.SubItems.Add(lvi.SubItems[j].Text);
                        }
                        item.Checked = lvi.Checked;
                        list_group_list.Tag = 1;
                        listView.Items.Add(item);
                    }

                    return true;
                }
            }
            catch
            {
            }

            return false;
        }

        void group_send_start()
        {
            group_thread = new System.Threading.Thread(send_group_thread);
            _group_should_stop = false;
            group_thread.Start();

            btn_group_start.Text = "停止";
        }

        void group_send_stop()
        {
            while (group_thread != null)
            {
                _group_should_stop = true;
                Application.DoEvents();
            }
        }

        private void btn_group_start_Click(object sender, EventArgs e)
        {
            if (group_thread == null)
            {
                group_send_start();
            }
            else
            {
                group_send_stop();
            }
        }

        private void group_send_text(string text)
        {
            if (chk_send_lua.Checked)
            {
                ThreadStatus status = lua.L_DoString(text);
                if (status != ThreadStatus.LUA_OK)
                {
                    throw new Exception(lua.ToString(-1));
                }
            }
            else
            {
                serial_send_text(text);
            }
        }

        private void group_send_text_try(string text)
        {
            try
            {
                group_send_text(text);
            }
            catch (Exception ex)
            {
                this.BeginInvoke((EventHandler)(delegate
                {
                    MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }));
            }
        }

        private void send_group_thread()
        {
            try
            {
                int index = -1;

                while (!_group_should_stop)
                {
                    string text = "";
                    int delay = -1;

                    this.Invoke((EventHandler)(delegate
                    {
                        for (int i = 0; i < list_group_list.Items.Count; i++)
                        {
                            list_group_list.Items[i].BackColor = Color.White;
                        }

                        for (int i = 0; i < list_group_list.Items.Count; i++)
                        {
                            index++;
                            if (index >= list_group_list.Items.Count)
                            {
                                index = 0;
                            }
                            if (list_group_list.Items[index].Checked)
                            {
                                list_group_list.Items[index].BackColor = Color.LightGreen;
                                text = list_group_list.Items[index].SubItems[1].Text;
                                delay = int.Parse(list_group_list.Items[index].SubItems[2].Text);
                                break;
                            }
                        }
                    }));

                    if (delay >= 0)
                    {
                        group_send_text(text);

                        while ((!_group_should_stop) && (delay > 0))
                        {
                            int ms = delay;
                            if (ms > 100) ms = 100;
                            System.Threading.Thread.Sleep(ms);
                            delay -= ms;
                        }
                    }
                    else
                    {
                        System.Threading.Thread.Sleep(100);
                    }
                }
            }
            catch (Exception ex)
            {
                this.BeginInvoke((EventHandler)(delegate
                {
                    group_send_stop();

                    MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }));
            }
            finally
            {
                group_thread = null;

                this.BeginInvoke((EventHandler)(delegate
                {
                    for (int i = 0; i < list_group_list.Items.Count; i++)
                    {
                        list_group_list.Items[i].BackColor = Color.White;
                    }

                    btn_group_start.Text = "循环";
                }));
            }
        }

        private void list_group_list_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            if ((e.Column == 0) && (list_group_list.Items.Count > 0))
            {
                int i = 0;
                for (i = 1; i < list_group_list.Items.Count; i++)
                {
                    if (list_group_list.Items[i].Checked != list_group_list.Items[0].Checked)
                    {
                        break;
                    }
                }
                list_group_list.Tag = 1;
                if (i < list_group_list.Items.Count)
                {
                    list_group_list.Items[0].Checked = true;
                }
                else
                {
                    list_group_list.Items[0].Checked = !list_group_list.Items[0].Checked;
                }
                for (i = 1; i < list_group_list.Items.Count; i++)
                {
                    list_group_list.Tag = 1;
                    list_group_list.Items[i].Checked = list_group_list.Items[0].Checked;
                }
            }
        }

        private void cbo_gen_code_SelectedIndexChanged(object sender, EventArgs e)
        {
            int codepage = int.Parse(cbo_gen_code.Text.Substring(0, cbo_gen_code.Text.IndexOf(',')));

            encoder = Encoding.GetEncoding(codepage);
            tooltip_code.SetToolTip(cbo_gen_code, cbo_gen_code.Text);
        }

        private string json_format(string json)
        {
            int indent = 0;
            bool esc_flag = false;
            bool str_flag = false;

            for (int i = 0; i < json.Length; i++)
            {
                if (esc_flag)
                {
                    esc_flag = false;
                }
                else if (json[i] == '\\')
                {
                    esc_flag = true;
                }
                else if (json[i] == '\"')
                {
                    str_flag = !str_flag;
                }
                else if (str_flag)
                {

                }
                else if (json[i] == '{')
                {
                    indent++;
                    string str_tmp = "\r\n" + new string('\t', indent);
                    json = json.Insert(i + 1, str_tmp);
                    i += str_tmp.Length;
                }
                else if (json[i] == '}')
                {
                    indent--;
                    string str_tmp = "\r\n" + new string('\t', indent);
                    json = json.Insert(i, str_tmp);
                    i += str_tmp.Length;
                }
                else if ((json[i] == ',') && (json[i + 1] != '{'))
                {
                    string str_tmp = "\r\n" + new string('\t', indent);
                    json = json.Insert(i + 1, str_tmp);
                    i += str_tmp.Length;
                }
            }

            return json;
        }

        private void lbl_group_export_Click(object sender, EventArgs e)
        {
            SaveFileDialog fileDialog = new SaveFileDialog();

            fileDialog.Title = "导出";
            fileDialog.Filter = "JSON文件(*.json)|*.json|所有文件(*.*)|*.*";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                List<ListItem> list = new List<ListItem>();
                for (int i = 0; i < list_group_list.Items.Count; i++)
                {
                    ListItem item = new ListItem();
                    item.Checked = list_group_list.Items[i].Checked;
                    item.Text = list_group_list.Items[i].SubItems[1].Text;
                    item.Delay = int.Parse(list_group_list.Items[i].SubItems[2].Text);
                    item.Note = list_group_list.Items[i].SubItems[3].Text;

                    list.Add(item);
                }

                JavaScriptSerializer js = new JavaScriptSerializer();
                string json = js.Serialize(list);

                File.WriteAllText(fileDialog.FileName, json_format(json));
            }
        }

        private void lbl_group_import_Click(object sender, EventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();

            try
            {
                fileDialog.Title = "导入";
                fileDialog.Filter = "JSON文件(*.json)|*.json|所有文件(*.*)|*.*";
                if (fileDialog.ShowDialog() == DialogResult.OK)
                {
                    string json = File.ReadAllText(fileDialog.FileName, TxtFileEncoder.GetEncoding(fileDialog.FileName));

                    JavaScriptSerializer js = new JavaScriptSerializer();
                    List<ListItem> list = js.Deserialize<List<ListItem>>(json);

                    list_group_list.BeginUpdate();
                    list_group_list.Items.Clear();
                    for (int i = 0; i < list.Count; i++)
                    {
                        ListViewItem item = new ListViewItem();
                        item.Checked = list[i].Checked;
                        item.Text = (i + 1).ToString();
                        item.SubItems.Add(list[i].Text);
                        item.SubItems.Add(((list[i].Delay > 0) ? list[i].Delay : 0).ToString());
                        item.SubItems.Add(list[i].Note);

                        list_group_list.Tag = 1;
                        list_group_list.Items.Add(item);
                    }
                    list_group_list.EndUpdate();
                }
            }
            catch (Exception ex)
            {
                MessageBoxEx.Show(this, ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        private void txt_group_send_KeyDown(object sender, KeyEventArgs e)
        {
            if ((e.KeyCode == Keys.Enter) && (e.Modifiers == Keys.Control))
            {
                group_send_text_try(txt_group_send.Text);
                e.SuppressKeyPress = true;
            }
        }

        private void btn_group_send_Click(object sender, EventArgs e)
        {
            group_send_text_try(txt_group_send.Text);
        }
    }

    public static class DoubleBuffer
    {
        public static void SetDoubleBuffer(this object lv, bool flag)
        {
            Type lvType = lv.GetType();
            System.Reflection.PropertyInfo pi = lvType.GetProperty("DoubleBuffered",
                System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
            pi.SetValue(lv, flag, null);
        }
    }

    public class ListItem
    {
        public bool Checked { get; set; }
        public string Text { get; set; }
        public int Delay { get; set; }
        public string Note { get; set; }
    }

    public class TxtFileEncoder
    {
        /// <summary>
        /// 给定文件的路径，读取文件的二进制数据，判断文件的编码类型
        /// </summary>
        /// <param name="FILE_NAME">文件路径</param>
        /// <returns>文件的编码类型</returns>
        public static System.Text.Encoding GetEncoding(string FILE_NAME)
        {
            FileStream fs = new FileStream(FILE_NAME, FileMode.Open, FileAccess.Read);
            Encoding r = GetEncoding(fs);
            fs.Close();
            return r;
        }

        /// <summary>
        /// 通过给定的文件流，判断文件的编码类型
        /// </summary>
        /// <param name="fs">文件流</param>
        /// <returns>文件的编码类型</returns>
        public static System.Text.Encoding GetEncoding(FileStream fs)
        {
            byte[] Unicode = new byte[] { 0xFF, 0xFE, 0x41 };
            byte[] UnicodeBIG = new byte[] { 0xFE, 0xFF, 0x00 };
            byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF }; //带BOM
            Encoding reVal = Encoding.Default;
            BinaryReader r = new BinaryReader(fs, System.Text.Encoding.Default);
            int i;
            int.TryParse(fs.Length.ToString(), out i);
            byte[] ss = r.ReadBytes(i);
            if (IsUTF8Bytes(ss) || (ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF))
            {
                reVal = Encoding.UTF8;
            }
            else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00)
            {
                reVal = Encoding.BigEndianUnicode;
            }
            else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41)
            {
                reVal = Encoding.Unicode;
            }
            r.Close();
            return reVal;
        }

        /// <summary>
        /// 判断是否是不带 BOM 的 UTF8 格式
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static bool IsUTF8Bytes(byte[] data)
        {
            int charByteCounter = 1;　 //计算当前正分析的字符应还有的字节数
            byte curByte; //当前分析的字节.
            for (int i = 0; i < data.Length; i++)
            {
                curByte = data[i];
                if (charByteCounter == 1)
                {
                    if (curByte >= 0x80)
                    {
                        //判断当前
                        while (((curByte <<= 1) & 0x80) != 0)
                        {
                            charByteCounter++;
                        }
                        //标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X　
                        if (charByteCounter == 1 || charByteCounter > 6)
                        {
                            return false;
                        }
                    }
                }
                else
                {
                    //若是UTF-8 此时第一位必须为1
                    if ((curByte & 0xC0) != 0x80)
                    {
                        return false;
                    }
                    charByteCounter--;
                }
            }

            if (charByteCounter > 1)
            {
                throw new Exception("非预期的byte格式");
            }
            return true;
        }
    }
}
